package com.godyhm.example.userendport.Protocol;

import android.util.Log;

import com.godyhm.example.userendport.Protocol.Protocol.RESULT_CODE;


public class ProtocolPacket {
	private final String TAG =getClass().getSimpleName();
	private byte[] mPacket;
	private int mTotalPacketSize;
	ProtocolHeader mPacketHeader;
	ProtocolData mPacketData;
	int mDataOffset;

	public ProtocolPacket() {
		mPacketHeader = new ProtocolHeader();
		mPacketData = new ProtocolData();
	}

	public ProtocolPacket(byte version, boolean compressed, byte frameType,
			byte frameSubType, byte connectionType, short messageId,
			short sessionId, short frameSequence, int dataSize) {
		byte[] data = {};
		mPacketHeader = new ProtocolHeader();
		mPacketData = new ProtocolData();
		serializePacket(version, compressed, frameType, frameSubType, connectionType,
				messageId, sessionId, frameSequence,dataSize,data);
	}

	public ProtocolPacket(byte version, boolean compressed, byte frameType,
			byte frameSubType, byte connectionType, short messageId,
			short sessionId, short frameSequence, int dataSize, byte[] data) {
		serializePacket(version, compressed, frameType, frameSubType, connectionType,
				messageId, sessionId, frameSequence,dataSize,data);
	}

	
	public ProtocolPacket(byte version, boolean compressed, byte frameType,
			byte frameSubType, byte connectionType, short messageId,
			short sessionId, short frameSequence,byte status) {
		serializePacket(version, compressed, frameType, frameSubType, connectionType,
				messageId, sessionId, frameSequence,status);
	}
	
	public Protocol.RESULT_CODE serializePacket(
			byte version, boolean compressed, byte frameType,
			byte frameSubType, byte connectionType, short messageId,
			short sessionId, short framesequence, int dataSize, byte[] data) {
		if (mPacket != null) {
			mPacket = null;
			mTotalPacketSize = 0;
		}

		int offset = 0;
		byte compressF = 0x0;
		mPacket = new byte[Protocol.PROTOCOL_HEADER_SENDER_SIZE+dataSize+1];
		if (compressed) {
			compressF = 0x1;
		}
		mPacket[offset++]=(byte) (Protocol.PROTOCOL_HEADER_SENDER_START>>8);
		mPacket[offset++]=(byte) Protocol.PROTOCOL_HEADER_SENDER_START;
		mPacket[offset++]=(byte)Protocol.PROTOCOL_HEADER_SENDER_SIZE;
		
		byte firstByte = (byte) (((version << 1) & 0xFE)|(compressF & 0x01));

		mPacket[offset++] = firstByte;
		mPacket[offset++] = frameType;
		mPacket[offset++] = frameSubType;
		mPacket[offset++] = connectionType;
		mPacket[offset++] = (byte) (messageId >> 8);
		mPacket[offset++] = (byte) (messageId);
		mPacket[offset++] = (byte) (sessionId >> 8);
		mPacket[offset++] = (byte) sessionId;
		mPacket[offset++] = (byte) (framesequence >> 8);
		mPacket[offset++] = (byte) framesequence;
		mPacket[offset++] = (byte) (dataSize >> 24);
		mPacket[offset++] = (byte) (dataSize >> 16);
		mPacket[offset++] = (byte) (dataSize >> 8);
		mPacket[offset++] = (byte) (dataSize);
		mTotalPacketSize = offset;
		if (data != null) {
			if ((dataSize < Protocol.MAXIMUM_FRAME_RAW_DATA_SIZE)) {
				System.arraycopy(data, 0, mPacket, offset, dataSize);
				mTotalPacketSize += dataSize;
				offset+=dataSize;
			} else {
				mPacket = null;
				mTotalPacketSize = 0;
				return Protocol.RESULT_CODE.RESULT_FAIL;
			}
		}
		byte sum=0;
		for(int i=0;i<offset;i++){
			sum+=mPacket[i];
		}
		mPacket[offset]=sum;
		mTotalPacketSize+=1;
		return Protocol.RESULT_CODE.RESULT_OK;
	}

	public Protocol.RESULT_CODE serializePacket(byte version, boolean compressed, 
			byte frameType,byte frameSubType, byte connectionType, 
			short messageId,short sessionId, short framesequence,
			byte status){
		if (mPacket != null) {
			mPacket = null;
			mTotalPacketSize = 0;
		}

		int offset = 0;
		byte compressF = 0x0;
		mPacket = new byte[Protocol.PROTOCOL_HEADER_REACT_SIZE+1+1];
		if (compressed) {
			compressF = 0x1;
		}
		mPacket[offset++]=(byte) (Protocol.PROTOCOL_HEADER_REACT_START>>8);
		mPacket[offset++]=(byte) Protocol.PROTOCOL_HEADER_REACT_START;
		mPacket[offset++]=(byte)Protocol.PROTOCOL_HEADER_REACT_SIZE;
		
		byte firstByte = (byte) (((version << 1) & 0xFE)|(compressF & 0x01));

		mPacket[offset++] = firstByte;
		mPacket[offset++] = frameType;
		mPacket[offset++] = frameSubType;
		mPacket[offset++] = connectionType;
		mPacket[offset++] = (byte) (messageId >> 8);
		mPacket[offset++] = (byte) (messageId);
		mPacket[offset++] = (byte) (sessionId >> 8);
		mPacket[offset++] = (byte) sessionId;
		mPacket[offset++] = (byte) (framesequence >> 8);
		mPacket[offset++] = (byte) framesequence;
		mPacket[offset++] = (byte) status;
		mTotalPacketSize = offset;
		byte sum=0;
		for(int i=0;i<offset;i++){
			sum+=mPacket[i];
		}
		mPacket[offset]=sum;
		mTotalPacketSize+=1;
		return Protocol.RESULT_CODE.RESULT_OK;
	}
	
	public Protocol.RESULT_CODE appendData(byte[] chunkData, int chunkDataSize) {
		if (mDataOffset + chunkDataSize <= mPacketData.getTotalDataBytes()) {
			System.arraycopy(chunkData, 0, mPacketData.getData(), mDataOffset,
					chunkDataSize);
			mDataOffset += chunkDataSize;
			return Protocol.RESULT_CODE.RESULT_OK;
		}
		return Protocol.RESULT_CODE.RESULT_FAIL;
	}

	public Protocol.RESULT_CODE deserializePacket(byte[] message,int messageSize) {
		int offset = 0;
		byte firstByte = message[offset++];
		Log.d(TAG, "firstByte="+Integer.toHexString(firstByte & 0xFF));
		byte secondByte = message[offset++];
		Log.i(TAG, "secondByte="+Integer.toHexString(secondByte & 0xFF));
		
		short packStart=(short) ((firstByte & 0xFF)<<8|(secondByte & 0xFF));
		Log.d(TAG, "packStart="+Integer.toHexString(packStart & 0xFFFF));
		if(packStart==Protocol.PROTOCOL_HEADER_SENDER_START){
			Log.d(TAG, "Protocol.PROTOCOL_HEADER_SENDER_START!");
			mPacketHeader.setHeaderType(Protocol.PROTOCOL_HEADER_TYPE_SEND);
		}else if(packStart==Protocol.PROTOCOL_HEADER_REACT_START){
			Log.d(TAG, "Protocol.PROTOCOL_HEADER_REACT_START!");
			mPacketHeader.setHeaderType(Protocol.PROTOCOL_HEADER_TYPE_REACT);
		}else{
			Log.e("ProtocolPacket", "didn't find valid packet symbol, return directly!");
			return RESULT_CODE.RESULT_UNKNOWN;
		}
		byte headerLength=message[offset++];
		
		mPacketHeader.setVersion((byte) ((message[offset] >>> 1)&0xFF));
		Log.d(TAG, "Version="+Integer.toHexString((message[offset] >>> 1) & 0xFF));
		boolean compressed = 1 == (message[offset++] & 0x01);
		Log.d(TAG, "compressed="+compressed);
		mPacketHeader.setCompressed(compressed);
		
		mPacketHeader.setFrameType(message[offset++]);
		mPacketHeader.setFrameSubType(message[offset++]);
		mPacketHeader.setConnectionType(message[offset++]);
		short msgId=(short) ((message[offset++]<<8)&0xFF00);
		msgId|=(message[offset++]&0xFF);
		mPacketHeader.setMessageId(msgId);
//		mPacketHeader.setMessageId((short)(message[offset]<<8|message[offset+1]));
//		offset+=2;
		short sessionId=(short) ((message[offset++]<<8)&0xFF00);
		sessionId|=(message[offset++]&0xFF);
		mPacketHeader.setSessionId(sessionId);
//		mPacketHeader.setSessionId((short)(message[offset]<<8|message[offset+1]));
//		offset+=2;
		short frameSequence=(short) ((message[offset++]<<8)&0xFF00);
		frameSequence|=(message[offset++]&0xFF);
		mPacketHeader.setFrameSequence(frameSequence);
//		mPacketHeader.setFrameSequence((short)(message[offset]<<8|message[offset+1]));
//		offset+=2;
		
		int dataSize = 0;
		if(mPacketHeader.getHeaderType()==Protocol.PROTOCOL_HEADER_TYPE_SEND){
			dataSize=message[offset++] << 24;
			dataSize |= message[offset++] << 16;
			dataSize |= message[offset++] << 8;
			dataSize |= message[offset++];
		}else if(mPacketHeader.getHeaderType()==Protocol.PROTOCOL_HEADER_TYPE_REACT){
			dataSize=1;
		}
		
		mPacketHeader.setDataSize(dataSize);

		int dataPayloadSize = messageSize - offset-1;
		if (dataPayloadSize != mPacketHeader.getDataSize()) {
			Log.i(TAG, "check data size failed, dataPayloadSize="+dataPayloadSize+", should be:"+mPacketHeader.getDataSize());
			return Protocol.RESULT_CODE.RESULT_FAIL;
		}

		byte[] data = null;
		if (dataPayloadSize != 0) {
			data = new byte[messageSize - offset-1];
			System.arraycopy(message, offset, data, 0, dataPayloadSize);
		}
		mPacketData.setData(data);
		mPacketData.setTotalDataBytes(dataPayloadSize);
		byte sum=0;
		for(int i=0;i<messageSize-1;i++){
			sum+=message[i];
		}
		if(sum==message[messageSize-1]){
			Log.i(TAG, "deserializePacket succeed!");
			return Protocol.RESULT_CODE.RESULT_OK;
		}else{
			Log.i(TAG, "check sum failed, calculate="+Integer.toHexString(sum&0xff)
					+", orgin="+Integer.toHexString(message[messageSize-1]&0xff));
			return Protocol.RESULT_CODE.RESULT_SUM_CHECK_FAIL;
		}
	}

	public byte getVersion() {
		return mPacketHeader.getVersion();
	}

	public boolean getIfCompress() {
		return mPacketHeader.isCompressed();
	}

	public byte getFrameType() {
		return mPacketHeader.getFrameType();
	}

	public byte getFrameSubType(){
		return mPacketHeader.getFrameSubType();
	}

	public byte getConnectionType(){
		return mPacketHeader.getConnectionType();
	}
	
	public SessionId getSessionId() {
		return mPacketHeader.getSessionId();
	}

	public short getFrameSequence(){
		return mPacketHeader.getFrameSequence();
	}
	
	public int getDataSize() {
		return mPacketHeader.getDataSize();
	}

	public short getMessageId() {
		return mPacketHeader.getMessageId();
	}

	public byte[] getData() {
		return mPacketData.getData();
	}

	void setTotalDataBytes(int dataBytes) {
		if (dataBytes != 0) {
			if (mPacketData.getData() != null) {
				mPacketData.setData(null);
			}
			mPacketData.setData(new byte[dataBytes]);
			mPacketData.setTotalDataBytes(dataBytes);
		}
	}

	int getTotalDataBytes() {
		return mPacketData.getTotalDataBytes();
	}

	public byte[] getPacket() {
		return mPacket;
	}

	public void setPacket(byte[] mPacket) {
		this.mPacket = mPacket;
	}

	public int getTotalPacketSize() {
		return mTotalPacketSize;
	}

	public int getPacketSize() {
		return mTotalPacketSize;
	}

	public void setTotalPacketSize(int mTotalPacketSize) {
		this.mTotalPacketSize = mTotalPacketSize;
	}
	
	public byte getHeadType(){
		return mPacketHeader.getHeaderType();
	}
	
	public String toString(){
		return mPacket==null?"":new String(mPacket);
	}
}